﻿using Elements;
using Elements.Links;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FlowChartCanvas
{
    /// <summary>
    /// 流程图画布
    /// </summary>
    public class FCCanvasV4:Control
    {
        public FCCanvasV4()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.UserPaint |
                ControlStyles.OptimizedDoubleBuffer, true);
        }

        #region 公共事件

        /// <summary>
        /// 连线时的状态提示
        /// </summary>
        public event Action<string> FCC_LinkState;
        /// <summary>
        /// 添加连线时，连线的颜色
        /// </summary>
        public event Func<Color> FCC_LinkColor;

        #endregion

        #region 公共属性

        /// <summary>
        /// 连线样式
        /// </summary>
        public string FCC_LinkType { get; set; } = "LineLink";
        /// <summary>
        /// 连线时，开始形状的连接点
        /// </summary>
        public ShapeBaseV2.ShapeLinkPoints FCC_LinkPointStart { get; set; } = ShapeBaseV2.ShapeLinkPoints.右;
        /// <summary>
        /// 连线时，结束形状的连接点
        /// </summary>
        public ShapeBaseV2.ShapeLinkPoints FCC_LinkPointEnd { get; set; } = ShapeBaseV2.ShapeLinkPoints.左;

        #endregion

        #region 公共方法

        //注：文章中说明，为了方便查看和演示有哪些方法和属性，所以固定开头，可依喜好不要此开头

        /// <summary>
        /// 向当前画布中添加形状
        /// </summary>
        /// <param name="sps"></param>
        public void FCC_AddShapes(List<ShapeBaseV2> sps)
        {
            if (sps == null || sps.Count == 0) return;
            foreach (var item in sps)
            {
                //根据ID去重
                if (!_shapes.Any(a => a.Id == item.Id))
                {
                    _shapes.Add(item);
                }
            }
            //令当前控件失效以重绘
            Invalidate();
        }
        
        /// <summary>
        /// 清空画布中的形状和连线
        /// </summary>
        public void FCC_Clear()
        {
            _shapes.Clear();
            _links.Clear();
            Invalidate();
        }

        /// <summary>
        /// 刷新当前画布
        /// </summary>
        public void FCC_Refresh()
        {
            Invalidate();
        }

        /// <summary>
        /// 开始连线
        /// </summary>
        public void FCC_StartLink()
        {
            _isAddLink = true;
            _selectedStartShape = null;
            _selectedEndShape = null;
            FCC_LinkState?.Invoke("请点击第1个形状");
        }

        /// <summary>
        /// 中止/停止连线
        /// </summary>
        public void FCC_StopLink()
        {
            _isAddLink = false;
            _selectedStartShape = null;
            _selectedEndShape = null;
            FCC_LinkState?.Invoke("");
            Invalidate();
        }

        //后续版本加。
        /// <summary>
        /// 获取当前画布所有形状
        /// </summary>
        /// <returns></returns>
        public List<ShapeBaseV2> FCC_GetAllShapes()
        {
            return _shapes;
        }

        #endregion

        #region 私有属性

        //注：文章中说明，不要直接暴露属性，通过方法来控制

        /// <summary>
        /// 形状集合
        /// </summary>
        List<ShapeBaseV2> _shapes = new List<ShapeBaseV2>();
        /// <summary>
        /// 连线集合
        /// </summary>
        List<LinkBaseV2> _links = new List<LinkBaseV2>();

        Bitmap _bmp;


        /// <summary>
        /// 当前是否有鼠标按下，且有矩形被选中
        /// </summary>
        bool _isMouseDown = false;
        /// <summary>
        /// 最后一次鼠标的位置
        /// </summary>
        Point _lastMouseLocation = Point.Empty;
        /// <summary>
        /// 当前被鼠标选中的矩形
        /// </summary>
        ShapeBaseV2 _selectedShape = null;

        /// <summary>
        /// 添加连线时选中的第一个形状
        /// </summary>
        ShapeBaseV2 _selectedStartShape = null;
        /// <summary>
        /// 添加连线时选中的第一个形状
        /// </summary>
        ShapeBaseV2 _selectedEndShape = null;
        /// <summary>
        /// 是否正添加连线
        /// </summary>
        bool _isAddLink = false;

        #endregion

        #region 私有方法

        #endregion

        #region 重写方法

        protected override void OnPaint(PaintEventArgs e)
        {
            //所有的形状、连线都会在此处绘制到控件上
            //创建内存绘图，将形状和连线绘制到此内存绘图上，然后再一次性绘制到控件上

            //注：后续再优化BMP，只有尺寸变化时才生成

            _bmp = new Bitmap(Width, Height);
            var g = Graphics.FromImage(_bmp);

            //设置显示质量
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

            g.Clear(BackColor);

            //绘制所有形状
            foreach (var sp in _shapes)
            {
                sp.Draw(g);
            }

            //绘制所有连线
            foreach (var ln in _links)
            {
                ln.Draw(g);
            }

            //绘制内存绘图到控件上
            e.Graphics.DrawImage(_bmp, new PointF(0, 0));

            //释放资源
            g.Dispose();

            base.OnPaint(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        { //当鼠标按下时

            //取最上方的形状
            var sp = _shapes.FindLast(a => a.Rect.Contains(e.Location));

            if (!_isAddLink)
            {
                //当前没有处理连线状态
                if (sp != null)
                {
                    //设置状态及选中矩形
                    _isMouseDown = true;
                    _lastMouseLocation = e.Location;
                    _selectedShape = sp;
                }
            }
            else
            {
                //正在添加连线

                if (_selectedStartShape == null)
                {
                    //证明没有矩形和圆形被选中则设置开始形状
                    if (sp != null)
                    {
                        //设置开始形状
                        _selectedStartShape = sp;
                    }
                    FCC_LinkState?.Invoke("请点击第2个形状");
                }
                else
                {
                    //判断第2个形状是否是第1个形状
                    if (sp != null)
                    {
                        //判断当前选中的矩形是否是第1步选中的矩形
                        if (_selectedStartShape.Id == sp.Id)
                        {
                            FCC_LinkState?.Invoke("不可选择同一个形状，请重新点击第2个形状");
                            return;
                        }
                    }

                    if (sp != null)
                    {
                        //设置结束形状
                        _selectedEndShape = sp;
                    }
                    else
                    {
                        return;
                    }

                    //两个形状都设置了，便添加一条新连线
                    switch(FCC_LinkType)
                    {
                        case "LineLink":
                            _links.Add(new LineLinkV2()
                            {
                                Id = "直线连线" + Guid.NewGuid().ToString(),//这里就不能用数量了，防止重复
                                BackgroundColor = FCC_LinkColor?.Invoke() ?? Color.Black,
                                StartShape = _selectedStartShape,
                                EndShape = _selectedEndShape,
                                StartShapeLinkPoint=FCC_LinkPointStart,
                                EndShapeLinkPoint=FCC_LinkPointEnd,
                            });
                            break;
                        case "BezierLink":
                            _links.Add(new BezierLinkV3()
                            {
                                Id = "贝塞尔曲线连线" + Guid.NewGuid().ToString(),//这里就不能用数量了，防止重复
                                BackgroundColor = FCC_LinkColor?.Invoke() ?? Color.Black,
                                StartShape = _selectedStartShape,
                                EndShape = _selectedEndShape,
                                StartShapeLinkPoint = FCC_LinkPointStart,
                                EndShapeLinkPoint = FCC_LinkPointEnd,
                            });
                            break;
                    }
                    //两个形状都已选择，结束添加连线状态
                    _isAddLink = false;
                    FCC_LinkState?.Invoke("");

                    //令当前控件失效以重绘
                    Invalidate();

                }

            }
            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {  
            //当鼠标移动时

            //如果处于添加连线时，则不移动形状
            if (_isAddLink) return;

            if (_isMouseDown)
            {
                //当且仅当：有鼠标按下且有矩形被选中时，才进行后续操作

                //改变选中矩形的位置信息，随着鼠标移动而移动

                //计算鼠标位置变化信息
                var moveX = e.Location.X - _lastMouseLocation.X;
                var moveY = e.Location.Y - _lastMouseLocation.Y;

                //将选中形状的位置进行同样的变化
                var oldXY = _selectedShape.Rect.Location;
                oldXY.Offset(moveX, moveY);
                _selectedShape.Rect = new Rectangle(oldXY, _selectedShape.Rect.Size);

                //记录当前鼠标位置
                _lastMouseLocation.Offset(moveX, moveY);

                //令当前控件失效以重绘
                Invalidate();
            }
            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        { 
            //当鼠标松开时
            if (_isMouseDown)
            {
                //当且仅当：有鼠标按下且有矩形被选中时，才进行后续操作

                //重置相关记录信息
                _isMouseDown = false;
                _lastMouseLocation = Point.Empty;
                _selectedShape = null;
            }
            base.OnMouseUp(e);
        }

        #endregion

    }
}
